﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.IO;

namespace CodeDOMTraining
{
    class Program
    {
        static void Main(string[] args)
        {
            //begin
            CodeCompileUnit myCompileUnit = new CodeCompileUnit();
            //create namespace
            CodeNamespace myNamespaces = new CodeNamespace("CodeDOMTraining");
            //import using statements
            myNamespaces.Imports.Add(new CodeNamespaceImport("System"));
            myNamespaces.Imports.Add(new CodeNamespaceImport("System.Collections.Generic"));
            myNamespaces.Imports.Add(new CodeNamespaceImport("System.Linq"));
            myNamespaces.Imports.Add(new CodeNamespaceImport("System.Text"));
            myNamespaces.Imports.Add(new CodeNamespaceImport("System.Threading.Tasks"));
            myCompileUnit.Namespaces.Add(myNamespaces);
            //create class
            CodeTypeDeclaration myClass = new CodeTypeDeclaration("Calculator");
            myClass.IsClass = true;
            myClass.TypeAttributes = System.Reflection.TypeAttributes.Public;
            //add the class to the namespace
            myNamespaces.Types.Add(myClass);
            //create fields
            CodeMemberField xField = new CodeMemberField();
            xField.Name = "x";
            xField.Type = new CodeTypeReference(typeof(double));
            myClass.Members.Add(xField);
            CodeMemberField yField = new CodeMemberField();
            xField.Name = "y";
            xField.Type = new CodeTypeReference(typeof(double));
            myClass.Members.Add(yField);

            //x property
            CodeMemberProperty xProperty = new CodeMemberProperty();
            xProperty.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            xProperty.Name = "X";
            xProperty.HasGet = true;
            xProperty.HasSet = true;
            xProperty.Type = new CodeTypeReference(typeof(System.Double));
            xProperty.GetStatements.Add(new CodeMethodReturnStatement(
                new CodeFieldReferenceExpression(new CodeThisReferenceExpression(),"x")));
            xProperty.SetStatements.Add(new CodeAssignStatement(
                new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "x"),
                new CodePropertySetValueReferenceExpression()));
            myClass.Members.Add(xProperty);

            //y property
            CodeMemberProperty yProperty = new CodeMemberProperty();
            xProperty.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            xProperty.Name = "Y";
            xProperty.HasGet = true;
            xProperty.HasSet = true;
            xProperty.Type = new CodeTypeReference(typeof(System.Double));
            xProperty.GetStatements.Add(new CodeMethodReturnStatement(
                new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "y")));
            xProperty.SetStatements.Add(new CodeAssignStatement(
                new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "y"),
                new CodePropertySetValueReferenceExpression()));
            myClass.Members.Add(yProperty);
            
            //create division method
            CodeMemberMethod divideMethod = new CodeMemberMethod();
            divideMethod.Name = "Divide";
            divideMethod.ReturnType = new CodeTypeReference(typeof(double));
            divideMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;

            //code for body of the division method
            CodeConditionStatement ifLogic = new CodeConditionStatement();

            ifLogic.Condition = new CodeBinaryOperatorExpression(
                new CodeFieldReferenceExpression(
                    new CodeThisReferenceExpression(), yProperty.Name),
                    CodeBinaryOperatorType.ValueEquality,
                    new CodePrimitiveExpression(0));

            ifLogic.TrueStatements.Add(new CodeMethodReturnStatement(
                new CodePrimitiveExpression(0)));

            ifLogic.FalseStatements.Add(new CodeMethodReturnStatement(
                new CodeBinaryOperatorExpression(
                    new CodeFieldReferenceExpression(
                        new CodeThisReferenceExpression(), xProperty.Name),
                        CodeBinaryOperatorType.Divide,
                        new CodeFieldReferenceExpression(
                            new CodeThisReferenceExpression(), yProperty.Name))));
            divideMethod.Statements.Add(ifLogic);

            //create the exponent method
            CodeMemberMethod exponentMethod = new CodeMemberMethod();
            exponentMethod.Name = "Exponent";
            exponentMethod.ReturnType = new CodeTypeReference(typeof(double));
            exponentMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;

            CodeParameterDeclarationExpression powerParameter = new CodeParameterDeclarationExpression(typeof(double),
            "power");
            exponentMethod.Parameters.Add(powerParameter);

            CodeMethodInvokeExpression callToMath = new CodeMethodInvokeExpression(
                new CodeTypeReferenceExpression("System.Math"),
                "Pow",
                new CodeFieldReferenceExpression(new CodeThisReferenceExpression(),
                    xProperty.Name), new CodeArgumentReferenceExpression("power"));

            exponentMethod.Statements.Add(new CodeMethodReturnStatement(callToMath));
            myClass.Members.Add(exponentMethod);

            //generate c# code
            CodeDomProvider provider = CodeDomProvider.CreateProvider("Jscript");
            CodeGeneratorOptions options = new CodeGeneratorOptions();
            options.BlankLinesBetweenMembers = false;
            options.BracingStyle = "C";

            using(StreamWriter sourceWriter = new StreamWriter(@"Calculator."+provider.FileExtension))
            {
                provider.GenerateCodeFromCompileUnit(myCompileUnit, sourceWriter, options);
            }
        }
    }
}
